package com.atguigu.tingshu.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.tingshu.account.client.UserAccountFeignClient;
import com.atguigu.tingshu.album.client.AlbumInfoFeignClient;
import com.atguigu.tingshu.album.client.TrackInfoFeignClient;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.order.OrderDerate;
import com.atguigu.tingshu.model.order.OrderDetail;
import com.atguigu.tingshu.model.order.OrderInfo;
import com.atguigu.tingshu.model.user.VipServiceConfig;
import com.atguigu.tingshu.order.helper.SignHelper;
import com.atguigu.tingshu.order.mapper.OrderDerateMapper;
import com.atguigu.tingshu.order.mapper.OrderDetailMapper;
import com.atguigu.tingshu.order.mapper.OrderInfoMapper;
import com.atguigu.tingshu.order.service.OrderInfoService;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.user.client.VipServiceConfigFeignClient;
import com.atguigu.tingshu.vo.account.AccountDeductVo;
import com.atguigu.tingshu.vo.order.OrderDerateVo;
import com.atguigu.tingshu.vo.order.OrderDetailVo;
import com.atguigu.tingshu.vo.order.OrderInfoVo;
import com.atguigu.tingshu.vo.order.TradeVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {

    @Autowired
    private OrderInfoMapper orderInfoMapper;

    @Autowired
    private UserInfoFeignClient userInfoFeignClient;

    @Autowired
    private AlbumInfoFeignClient albumInfoFeignClient;

    @Autowired
    private VipServiceConfigFeignClient vipServiceConfigFeignClient;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private TrackInfoFeignClient trackInfoFeignClient;

    @Autowired
    private OrderDerateMapper orderDerateMapper;

    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private RabbitService rabbitService;

    @Autowired
    private UserAccountFeignClient userAccountFeignClient;

    @Override
    public OrderInfoVo trade(TradeVo tradeVo, Long userId) {
        //  声明原始金额变量;
        BigDecimal originalAmount = new BigDecimal("0.00");
        //  减免金额
        BigDecimal derateAmount = new BigDecimal("0.00");
        //  实际金额
        BigDecimal orderAmount = new BigDecimal("0.00");
        //  订单明细
        List<OrderDetailVo> orderDetailVoList = new ArrayList<>();
        //  减免明细
        List<OrderDerateVo> orderDerateVoList = new ArrayList<>();
        //  远程调用获取用户信息
        Result<UserInfoVo> userInfoVoResult = userInfoFeignClient.getUserInfoVo(userId);
        Assert.notNull(userInfoVoResult, "用户信息为空");
        UserInfoVo userInfoVo = userInfoVoResult.getData();
        Assert.notNull(userInfoVo, "用户信息为空");

        //  判断条件：
        if (tradeVo.getItemType().equals(SystemConstant.ORDER_ITEM_TYPE_ALBUM)) {
            //  购买专辑  不能重复下单；需要下单的时候，校验！不能重复购买！
            //  获取专辑对象
            Result<AlbumInfo> albumInfoResult = albumInfoFeignClient.getAlbumInfo(tradeVo.getItemId());
            Assert.notNull(albumInfoResult, "专辑信息为空");
            AlbumInfo albumInfo = albumInfoResult.getData();
            Assert.notNull(albumInfo, "专辑信息为空");

            //  用户如果购买过专辑则将信息添加到user_paid_album 表中;
            Result<Boolean> isPaidAlbumResult = userInfoFeignClient.isPaidAlbum(tradeVo.getItemId());
            Assert.notNull(isPaidAlbumResult, "isPaidAlbumResult为空");
            Boolean result = isPaidAlbumResult.getData();
            if (result) {
                //  用户购买过专辑，不能重复购买！
                throw new GuiguException(ResultCodeEnum.REPEAT_BUY_ERROR);
            }
            //  原始金额；
            originalAmount = albumInfo.getPrice();
            //  当前用户属于vip
            if (userInfoVo.getIsVip() == 1 && userInfoVo.getVipExpireTime().after(new Date())) {
                //  判断是否有打折
                if (albumInfo.getVipDiscount().compareTo(new BigDecimal("-1")) == 1) {
                    //  计算原始金额 ，折扣金额，减免金额，实际金额
                    //  1000; 8折; 实付：800 折扣：200
                    derateAmount = originalAmount.subtract(originalAmount.multiply(albumInfo.getVipDiscount())).setScale(2, RoundingMode.HALF_UP);
                    //  原始金额*(10-8)=2000/10=200;
                }
            } else {
                if (albumInfo.getDiscount().compareTo(new BigDecimal("-1")) == 1) {
                    //  计算原始金额 ，折扣金额，减免金额，实际金额
                    //  1000; 8折; 实付：800 折扣：200
                    derateAmount = originalAmount.subtract(originalAmount.multiply(albumInfo.getDiscount())).setScale(2, RoundingMode.HALF_UP);
                    //  原始金额*(10-8)=2000/10=200;
                }
            }
            //  实际金额
            orderAmount = originalAmount.subtract(derateAmount);

            //  赋值订单明细集合：orderDetailVoList
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemId(albumInfo.getId());
            orderDetailVo.setItemName(albumInfo.getAlbumTitle());
            orderDetailVo.setItemUrl(albumInfo.getCoverUrl());
            orderDetailVo.setItemPrice(orderAmount);
            orderDetailVoList.add(orderDetailVo);

            //  赋值减免明细.
            if (derateAmount.compareTo(new BigDecimal("0.00")) != 0) {
                OrderDerateVo orderDerateVo = new OrderDerateVo();
                orderDerateVo.setDerateAmount(derateAmount);
                orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_ALBUM_DISCOUNT);
                orderDerateVo.setRemarks("专辑折扣");
                orderDerateVoList.add(orderDerateVo);
            }
        } else if (tradeVo.getItemType().equals(SystemConstant.ORDER_ITEM_TYPE_TRACK)) {
            //  购买声音
            if (tradeVo.getTrackCount() < 0 ){
                //如果小于0抛异常
                throw new GuiguException(ResultCodeEnum.DATA_ERROR);
            }
            //订单明细 需要从当前声音id开始，往后数10集！ 获得声音列表
            Result<List<TrackInfo>> trackInfoListResult = trackInfoFeignClient.findPaidTrackInfoList(tradeVo.getItemId(), tradeVo.getTrackCount());
            Assert.notNull(trackInfoListResult,"trackInfoListResult为空");
            List<TrackInfo> trackInfoList = trackInfoListResult.getData();
            Assert.notNull(trackInfoList,"trackInfoList为空");
            //等于或大于0；计算价格，原始金额  实际金额 减免金额【不支持折扣】
            //trackid=48241 trackcount=10  单价*数量  单价 = albuminfo.price
            TrackInfo trackInfo = trackInfoList.get(0);
            Result<AlbumInfo> albumInfoResult = albumInfoFeignClient.getAlbumInfo(trackInfo.getAlbumId());
            Assert.notNull(albumInfoResult, "albumInfoResult为空");
            AlbumInfo albumInfo = albumInfoResult.getData();
            Assert.notNull(albumInfo, "albumInfo为空");
            //赋值订单明细
            orderDetailVoList = trackInfoList.stream().map(track ->{
                //创建订单明细对象
                OrderDetailVo orderDetailVo = new OrderDetailVo();
                //赋值
                orderDetailVo.setItemId(track.getId());
                orderDetailVo.setItemPrice(albumInfo.getPrice());
                orderDetailVo.setItemUrl(track.getCoverUrl());
                orderDetailVo.setItemName(track.getTrackTitle());
                //返回对象
                return orderDetailVo;
            }).collect(Collectors.toList());
            //计算原生金额
            originalAmount = tradeVo.getTrackCount() == 0?albumInfo.getPrice():albumInfo.getPrice().multiply(new BigDecimal(tradeVo.getTrackCount()));
            orderAmount = originalAmount;
        } else {
            //  购买vip {"itemType":"1003","itemId":2} vip 是可以重复下单的！
            //  获取vipServiceConfig对象。
            Result<VipServiceConfig> vipServiceConfigResult = vipServiceConfigFeignClient.getVipServiceConfig(tradeVo.getItemId());
            Assert.notNull(vipServiceConfigResult, "vipServiceConfigResult为空");
            VipServiceConfig vipServiceConfig = vipServiceConfigResult.getData();
            Assert.notNull(vipServiceConfig, "vipServiceConfig为空");
            //  vip 原始金额
            originalAmount = vipServiceConfig.getPrice();
            //  实际金额
            orderAmount = vipServiceConfig.getDiscountPrice();
            //  减免金额
            derateAmount = originalAmount.subtract(orderAmount);

            //  赋值订单明细
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemId(vipServiceConfig.getId());
            orderDetailVo.setItemName(vipServiceConfig.getName());
            orderDetailVo.setItemUrl(vipServiceConfig.getImageUrl());
            orderDetailVo.setItemPrice(originalAmount);
            orderDetailVoList.add(orderDetailVo);

            //  减免明细
            if (derateAmount.compareTo(new BigDecimal("0.00")) != 0){
                OrderDerateVo orderDerateVo = new OrderDerateVo();
                orderDerateVo.setDerateAmount(derateAmount);
                orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_VIP_SERVICE_DISCOUNT);
                orderDerateVo.setRemarks("vip服务折扣");
                orderDerateVoList.add(orderDerateVo);
            }
        }
        //  创建对象
        OrderInfoVo orderInfoVo = new OrderInfoVo();
        String tradeNo = UUID.randomUUID().toString().replaceAll("-","");
        //  将这个tradeNo 存储到缓存中. tradeNo=UUID;字符串.
        String tradeNoKey = "tradeNo:" + userId;
        this.redisTemplate.opsForValue().set(tradeNoKey, tradeNo, 5, TimeUnit.MINUTES);
        orderInfoVo.setTradeNo(tradeNo);
        orderInfoVo.setPayWay("");
        orderInfoVo.setItemType(tradeVo.getItemType());
        orderInfoVo.setOriginalAmount(originalAmount);
        orderInfoVo.setOrderAmount(orderAmount);
        orderInfoVo.setDerateAmount(derateAmount);
        orderInfoVo.setOrderDetailVoList(orderDetailVoList);
        orderInfoVo.setOrderDerateVoList(orderDerateVoList);
        //  时间戳
        orderInfoVo.setTimestamp(SignHelper.getTimestamp());
        //  签名: 防止用户通过非法手段篡改提交订单信息的！ 保证接口数据安全性！
        //  使用工具类：需要一个 map 集合;
        Map map = JSON.parseObject(JSON.toJSONString(orderInfoVo), Map.class);
        //  将 orderInfoVo 转换为 map 集合，将这个map 进行排序，然后将map的value使用|进行拼接。再拼接上固定的字符串, 最近将这个字符串进行md5加密。
        String sign = SignHelper.getSign(map);
        orderInfoVo.setSign(sign);
        //  返回数据
        return orderInfoVo;
    }

    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public String submitOrder(OrderInfoVo orderInfoVo, Long userId) {
        //校验流水号和签名
        //获取订单流水号
        String tradeNo = orderInfoVo.getTradeNo();
        //让页面流水号与redis 流水号进行比较
        String tradeNoKey = "tradeNo:" + userId;
        //使用lua脚本，key与键值相匹配的时候，再删除
        String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
                "then\n" +
                "    return redis.call(\"del\",KEYS[1])\n" +
                "else\n" +
                "    return 0\n" +
                "end";
        //如果流水号一致， 页面与redis的值一样
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(script);
        redisScript.setResultType(Long.class);
        long count = (long) redisTemplate.execute(redisScript, Arrays.asList(tradeNoKey), tradeNo);
        if (count == 0) {
            //说明重复购买
            throw new GuiguException(ResultCodeEnum.REPEAT_BUY_ERROR);
        }
        // 校验签名
        //需要将orderInfoVo转换为map集合
        Map map = JSON.parseObject(JSON.toJSONString(orderInfoVo), Map.class);
        //统一设置为默认的一种支付方式
        map.put("payWay", "");
        SignHelper.checkSign(map);
        //生成一个订单编号
        String orderNo = UUID.randomUUID().toString().replaceAll("-", "");
        //判断支付类型
        if (SystemConstant.ORDER_PAY_ACCOUNT.equals(orderInfoVo.getPayWay())){
            //属于余额支付，使用余额进行扣款，检查与锁定余额，保存订单信息，保存用户购买记录
            //检查与锁定余额
            AccountDeductVo accountDeductVo = new AccountDeductVo();
            accountDeductVo.setOrderNo(orderNo);
            accountDeductVo.setUserId(userId);
            accountDeductVo.setAmount(orderInfoVo.getOrderAmount());
            accountDeductVo.setContent("购买商品");
            //远程调用 检查与锁定可用金额
            Result result = userAccountFeignClient.checkAndDeduct(accountDeductVo);
            //判断是否有足够的余额
            if (200 !=result.getCode()){
                //抛出异常
                throw new GuiguException(result.getCode(),result.getMessage());
            }
            //保存订单
            OrderInfo orderInfo = this.saveOrder(orderInfoVo,userId,orderNo);
            //保存用户购买记录
            UserPaidRecordVo userPaidRecordVo = new UserPaidRecordVo();
            userPaidRecordVo.setOrderNo(orderNo);
            userPaidRecordVo.setUserId(userId);
            userPaidRecordVo.setItemType(orderInfoVo.getItemType());
            //【专辑id，vip服务配置id，下单的时候只能购买一个，这个明细数据就是一条】 声音id（应该是集合）
            //以上id都有 orderInfoVo.getOrderDetailVoList()
            List<Long> itemIdList = orderInfoVo.getOrderDetailVoList().stream().map(OrderDetailVo::getItemId).collect(Collectors.toList());
            userPaidRecordVo.setItemIdList(itemIdList);

            //需要将这些数据对应的保存到用户交易记录 user_paid_album    user_paid_track   user_vip_service
            //远程调用
            Result userResult = userInfoFeignClient.savePaidRecord(userPaidRecordVo);
            //判断是否保存交易记录成功
            if (200 !=userResult.getCode()){
                //抛出异常
                throw new GuiguException(userResult.getCode(),userResult.getMessage());
            }
        }else {
            //在线支付 调用微信支付接口功能
            this.saveOrder(orderInfoVo,userId,orderNo);
        }
        //返回订单编号
        return orderNo;
    }

    /**
     * 保存订单
     * @param orderInfoVo
     * @param userId
     * @param orderNo
     * @return
     */

    public OrderInfo saveOrder(OrderInfoVo orderInfoVo, Long userId, String orderNo) {
        //创建orderInfo对象
        OrderInfo orderInfo = new OrderInfo();
        //属性拷贝
        BeanUtils.copyProperties(orderInfoVo, orderInfo);
        //赋值用户Id
        orderInfo.setUserId(userId);
        //赋值订单标题
        orderInfo.setOrderTitle(orderInfoVo.getOrderDetailVoList().get(0).getItemName());
        //赋值订单编号
        orderInfo.setOrderNo(orderNo);
        //赋值订单状态
        orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_UNPAID);
        orderInfoMapper.insert(orderInfo);
        //订单明细表
        List<OrderDetailVo> orderDetailVoList = orderInfoVo.getOrderDetailVoList();
        if (!CollectionUtils.isEmpty(orderDetailVoList)){
            for (OrderDetailVo orderDetailVo : orderDetailVoList){
                //创建订单明细对象
                OrderDetail orderDetail = new OrderDetail();
                //  属性拷贝：
                //  vip:item_id=vip_service_config.id;
                //  track: item_id=track_info.id;
                //  album: item_id=album_info.id;
                BeanUtils.copyProperties(orderDetailVo, orderDetail);
                orderDetail.setOrderId(orderInfo.getId());
                orderDetailMapper.insert(orderDetail);
            }
        }
        //订单减免
        List<OrderDerateVo> orderDerateVoList = orderInfoVo.getOrderDerateVoList();
        if (!CollectionUtils.isEmpty(orderDerateVoList)){
            for (OrderDerateVo orderDerateVo : orderDerateVoList){
                //创建订单减免对象
                OrderDerate orderDerate = new OrderDerate();
                //属性拷贝
                BeanUtils.copyProperties(orderDerateVo, orderDerate);
                orderDerate.setOrderId(orderInfo.getId());
                orderDerateMapper.insert(orderDerate);
            }
        }
        //判断支付类型
        if (orderInfoVo.getPayWay().equals(SystemConstant.ORDER_PAY_ACCOUNT)){
            //修改订单状态
            this.payOrderStatus(orderInfo.getId(), SystemConstant.ORDER_STATUS_PAID);
        }else {
            //发送一个消息，记录当前取消订单
            this.rabbitService.sendDealyMessage(MqConst.EXCHANGE_CANCEL_ORDER, MqConst.ROUTING_CANCEL_ORDER, orderInfo.getId(), MqConst.CANCEL_ORDER_DELAY_TIME);
        }
        return orderInfo;
    }

    /**
     *  修改订单状态
     * @param id
     * @param orderStatusPaid
     */
    private void payOrderStatus(Long id, String orderStatus) {
        //查询订单对象
        OrderInfo orderInfo = this.getById(id);
        // 如果已经修改为支付，那么就不需要再修改
        if (orderInfo.getOrderStatus().equals(SystemConstant.ORDER_STATUS_PAID)){
            return;
        }
        //赋值为已支付
        orderInfo.setOrderStatus(orderStatus);
        //修改为已支付
        this.updateById(orderInfo);
    }

    @Override
    public void orderCancel(Long orderId) {
        //  修改订单状态。
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setId(orderId);
        orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_CANCEL);
        this.updateById(orderInfo);
    }

    @Override
    public OrderInfo getOrderInfoByOrderNo(String orderNo) {
        //查看订单对象
        OrderInfo orderInfo = orderInfoMapper.selectOne(new LambdaQueryWrapper<OrderInfo>().eq(OrderInfo::getOrderNo, orderNo));
        orderInfo.setPayWayName(this.getPayWayName(orderInfo.getPayWay()));
        //返回数据
        return orderInfo;
    }

    /**
     * 获取支付名称
     * @param payWay
     * @return
     */
    private String getPayWayName(String payWay) {
        return payWay.equals(SystemConstant.ORDER_PAY_ACCOUNT)?"余额":"在线";
    }

    /**
     *  获取订单分页列表
     * @param infoPage
     * @param userId
     * @param orderStatus
     * @return
     */
   @Override
    public IPage<OrderInfo> findUserPage(Page<OrderInfo> infoPage, Long userId, String orderStatus) {
        //调用mapper
        IPage<OrderInfo> ipage = orderInfoMapper.selectUserPage(infoPage, userId, orderStatus);
        //设置一个订单名称
        ipage.getRecords().stream().forEach(orderInfo -> {
            orderInfo.setOrderStatusName(this.getOrderStatusName(orderInfo.getOrderStatus()));
        });
        return ipage;
    }

    /**
     *  获取订单状态名称   0901-未支付 0902-已支付 0903-已取消
     * @param orderStatus
     * @return
     */
    private String getOrderStatusName(String orderStatus) {
        //三元表达式
        return orderStatus.equals(SystemConstant.ORDER_STATUS_UNPAID)?"未支付":orderStatus.equals(SystemConstant.ORDER_STATUS_PAID)?"已支付":"已取消";
    }

}
